package com.tyqx.hashchainbiz.controller;

// import com.alibaba.fastjson.JSON;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.tyqx.hashchainbean.module.HashIndex;
import com.tyqx.hashchainbean.module.TrackUser;
import com.tyqx.hashchainbean.vo.BlockChainVO;
import com.tyqx.hashchainbean.vo.MerkleRootVO;
import com.tyqx.hashchainbiz.controller.config.JwtTokenUtil;
import com.tyqx.hashchainbiz.service.HashIndexService;
import com.tyqx.hashchainbiz.service.TrackUserService;
import com.tyqx.hashchainutil.common.*;
import com.tyqx.hashchainutil.org.app.util.Util;
import hashchainutil.common.*;
import io.swagger.annotations.Api;
import org.hyperledger.fabric.sdk.security.CryptoPrimitives;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.nio.file.Path;
import java.text.SimpleDateFormat;
import java.util.*;

@Api(description = "区块链验证")
@RestController
@RequestMapping("/verify")
@CrossOrigin
public class VerifyController {


    @Resource
    private TrackUserService trackUserService;

    @Resource
    private HashIndexService hashIndexService;

    @Resource
    private MongoTemplate mongoTemplate;

    @Resource
    private JwtTokenUtil jwtTokenUtil;


    Logger logger = LoggerFactory.getLogger(this.getClass());


    /*@RequestMapping(value = "/verifyHashValue", method = RequestMethod.POST,
            produces = "application/json")
    @ResponseBody
    public Dto verifyHashValue(@RequestBody String jsonObject) throws Exception {
        String caType = "msp";
        Long orgIndex = 0L;
        String channelName="hashchainchannel";
        JSONObject jsonObject1 = new JSONObject(jsonObject);
        jsonObject1 = JsonUtil.getSortJson(jsonObject1);
        String jsonObject2 = jsonObject1.toString();
        CryptoPrimitives suite = new CryptoPrimitives();
        suite.init();
        byte[] hashBytes = suite.hash(jsonObject2.getBytes());

        String hashValue = Base64.getEncoder().encodeToString(hashBytes);

        List<HashIndex> hashIndexList = hashIndexService.getHashIndexByHashValue(hashValue);
        HashIndex hashIndex = hashIndexList.get(0);

        BlockData blockData = new BlockData();
        Map<String, String> map = blockData.getBlockByNum(hashIndex.getBlockNum(),channelName);
        String blockDataStr = map.get("blockDataStr");
        String blockNum=map.get("blockNum");
        JsonObject blockDataJson = new JsonParser().parse(blockDataStr).getAsJsonObject();
        JsonObject hashItemJson = blockData.getHashItemByTxId(blockDataJson, hashIndex.getTxId());
        String blockHashValue = hashItemJson.get("hashValue").toString();

        blockHashValue = blockHashValue.substring(blockHashValue.indexOf("\"") + 1, blockHashValue.lastIndexOf("\""));
        String blockHashSignature = hashItemJson.get("hashSignature").toString();
        blockHashSignature = blockHashSignature.substring(blockHashSignature.indexOf("\"") + 1, blockHashSignature.lastIndexOf("\""));
        if (blockHashValue.equals(hashValue)) {
            System.out.println("hash match");
        }

        Map<String, Object> pathMap = Util.readUserContext(caType, jsonObject1.getString("userName"), orgIndex);
        Path caPath = (Path) pathMap.get("caPath");
        Path clientCertPath = (Path) pathMap.get("clientCertPath");
        Path clientKeyPath = (Path) pathMap.get("clientKeyPath");

        CryptoUtil cryptoUtil = new CryptoUtil();

        boolean flag = cryptoUtil.verify(clientCertPath, blockHashSignature, blockHashValue);
        if (!flag){
            return DtoUtil.returnFail("未查询到该交易","100001");
        }else{
            return DtoUtil.returnSuccess("验证区块链成功",blockNum);
        }
    }*/

    @RequestMapping(value = "/verifyData", method = RequestMethod.POST,
            produces = "application/json")
    @ResponseBody
    public Dto verifyData(@RequestBody String jsonObject, HttpServletRequest request) throws Exception {
        try {

            String jwtToken = request.getHeader("Authorization");
            String tokenUserName = jwtTokenUtil.getUsernameFromToken(jwtToken);

            String caType = "msp";
            Long orgIndex = 0L;
            String channelName = "hashchainchannel";

            JSONObject jsonObject1 = new JSONObject(jsonObject);
            String userName=jsonObject1.getString("userName");

            TrackUser trackUser=trackUserService.getUserByCode(userName);
            if(EmptyUtils.isEmpty(trackUser)){
                String msg="用户找不到";
                return DtoUtil.returnFail(msg,"100006");
            }

            if(!tokenUserName.equals(userName)){
                String msg="用户名跟token用户名不匹配";
                return DtoUtil.returnFail(msg,"100005");
            }

            jsonObject1 = JsonUtil.getSortJson(jsonObject1);
            String jsonObject2 = jsonObject1.toString();
            CryptoPrimitives suite = new CryptoPrimitives();
            suite.init();
            byte[] hashBytes = suite.hash(jsonObject2.getBytes());

            String hashValue = Base64.getEncoder().encodeToString(hashBytes);

            List<HashIndex> hashIndexList = hashIndexService.getHashIndexByHashValue(hashValue);
            HashIndex hashIndex = null;
            if (hashIndexList.size() > 0) {
                hashIndex = hashIndexList.get(0);
            } else {
                return DtoUtil.returnFail("未查询到该交易", "100002");
            }

            BlockData blockData = new BlockData();
            Map<String, String> map = blockData.getBlockByNum(hashIndex.getBlockNum(), channelName);
            String blockDataStr = map.get("blockDataStr");
            String blockNum = map.get("blockNum");
            JsonObject blockDataJson = new JsonParser().parse(blockDataStr).getAsJsonObject();
            JsonObject hashItemJson = blockData.getHashItemByTxId(blockDataJson, hashIndex.getTxId());
            String blockHashValue = hashItemJson.get("hashValue").toString();

            blockHashValue = blockHashValue.substring(blockHashValue.indexOf("\"") + 1, blockHashValue.lastIndexOf("\""));
            String blockHashSignature = hashItemJson.get("hashSignature").toString();
            blockHashSignature = blockHashSignature.substring(blockHashSignature.indexOf("\"") + 1, blockHashSignature.lastIndexOf("\""));


            Map<String, Object> pathMap = Util.readUserContext(caType, userName, orgIndex);
            //Path caPath = (Path) pathMap.get("caPath");
            Path clientCertPath = (Path) pathMap.get("clientCertPath");
            //Path clientKeyPath = (Path) pathMap.get("clientKeyPath");

            CryptoUtil cryptoUtil = new CryptoUtil();

            boolean flag = cryptoUtil.verify(clientCertPath, blockHashSignature, blockHashValue,mongoTemplate);

            String collectionName = "merkleRoot";

            List<String> hashValueList = new ArrayList<>();

            Criteria criteria = Criteria.where("rootHashValue").is(blockHashValue);
            Query query = Query.query(criteria);
            List<MerkleRootVO> MerkleRootVO = mongoTemplate.find(query, MerkleRootVO.class, collectionName);
            /*if (MerkleRootVO.size() > 0) {
                hashValueList = MerkleRootVO.get(0).getHashValueList();
            } else {
                hashValueList = hashIndexService.getHashValueList(blockHashValue);
                if (hashValueList.size() > 0) {
                    MerkleRootVO merkleRootVO = new MerkleRootVO();
                    merkleRootVO.setHashValueList(hashValueList);
                    merkleRootVO.setRootHashValue(blockHashValue);
                    mongoTemplate.insert(merkleRootVO, "merkleRoot");
                }
            }

            String rootHashValue = MerkleUtil.getMerkleRoot(hashValueList, suite);*/

            List<List<String>> merkleTree=new ArrayList<>();

            if (MerkleRootVO.size() > 0) {
                merkleTree = MerkleRootVO.get(0).getMerkleTree();
            } else {
                hashValueList = hashIndexService.getHashValueList(blockHashValue);
                merkleTree = MerkleUtil.computeMerkleTree(hashValueList, suite);
                if (merkleTree.size() > 0) {
                    MerkleRootVO merkleRootVO = new MerkleRootVO();
                    //merkleRootVO.setHashValueList(hashValueList);
                    merkleRootVO.setMerkleTree(merkleTree);
                    merkleRootVO.setRootHashValue(blockHashValue);
                    mongoTemplate.insert(merkleRootVO, "merkleRoot");
                    /*long flag1=mongoTemplate.count(new Query(),"merkleRoot");
                    if(flag1>0){

                    }*/
                }
            }
            boolean varifyResult = MerkleUtil.verifyMerkleTree(merkleTree, hashValue, suite);
            //String rootHashValue = MerkleUtil.getMerkleRoot(hashValueList, suite);

            if (varifyResult  && flag) {
                BlockChainVO blockChainVO=new BlockChainVO();
                blockChainVO.setTxId(hashIndex.getTxId());
                blockChainVO.setHashValue(blockHashValue);
                blockChainVO.setBlockNum(blockNum);
                blockChainVO.setTxTimeStamp(hashIndex.getTxTimeStamp());
                blockChainVO.setUserName(userName);

                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                String txTimeStamp = sdf.format(hashIndex.getTxTimeStamp());

                String successMsg="验证区块链成功,数据位于区块:"+blockNum+",上链时间:"+txTimeStamp+",数据签名人:"+userName;
                return DtoUtil.returnSuccess(successMsg);
            } else {
                return DtoUtil.returnFail("验证区块链失败", "100001");
            }
        }catch (Exception e){
            return DtoUtil.returnFail("验证区块链错误","100003");
        }
    }


    @RequestMapping(value = "/verifyHashValue", method = RequestMethod.POST,
            produces = "application/json")
    @ResponseBody
    public Dto verifyHashChainByHashValue(@RequestParam(value = "hashValue",required = true) String hashValue,@RequestParam(value = "userName",required = true) String userName,HttpServletRequest request) throws Exception {
        try {
            String jwtToken = request.getHeader("Authorization");
            String tokenUserName = jwtTokenUtil.getUsernameFromToken(jwtToken);
            String caType = "msp";
            Long orgIndex = 0L;
            String channelName = "hashchainchannel";
            CryptoPrimitives suite = new CryptoPrimitives();
            suite.init();

            TrackUser trackUser=trackUserService.getUserByCode(userName);
            if(EmptyUtils.isEmpty(trackUser)){
                String msg="用户找不到";
                return DtoUtil.returnFail(msg,"100006");
            }

            if(!tokenUserName.equals(userName)){
                String msg="用户名跟token用户名不匹配";
                return DtoUtil.returnFail(msg,"100005");
            }


            List<HashIndex> hashIndexList = hashIndexService.getHashIndexByHashValue(hashValue);
            HashIndex hashIndex = null;
            if (hashIndexList.size() > 0) {
                hashIndex = hashIndexList.get(0);
            } else {
                return DtoUtil.returnFail("未查询到该交易", "100002");
            }

            BlockData blockData = new BlockData();
            Map<String, String> map = blockData.getBlockByNum(hashIndex.getBlockNum(), channelName);
            String blockDataStr = map.get("blockDataStr");
            String blockNum = map.get("blockNum");
            JsonObject blockDataJson = new JsonParser().parse(blockDataStr).getAsJsonObject();
            JsonObject hashItemJson = blockData.getHashItemByTxId(blockDataJson, hashIndex.getTxId());
            String blockHashValue = hashItemJson.get("hashValue").toString();

            blockHashValue = blockHashValue.substring(blockHashValue.indexOf("\"") + 1, blockHashValue.lastIndexOf("\""));
            String blockHashSignature = hashItemJson.get("hashSignature").toString();
            blockHashSignature = blockHashSignature.substring(blockHashSignature.indexOf("\"") + 1, blockHashSignature.lastIndexOf("\""));


            Map<String, Object> pathMap = Util.readUserContext(caType, userName, orgIndex);
            //Path caPath = (Path) pathMap.get("caPath");
            Path clientCertPath = (Path) pathMap.get("clientCertPath");
            //Path clientKeyPath = (Path) pathMap.get("clientKeyPath");

            CryptoUtil cryptoUtil = new CryptoUtil();

            boolean flag = cryptoUtil.verify(clientCertPath, blockHashSignature, blockHashValue,mongoTemplate);


            String collectionName = "merkleRoot";

            List<String> hashValueList = new ArrayList<>();

            Criteria criteria = Criteria.where("rootHashValue").is(blockHashValue);
            Query query = Query.query(criteria);
            List<MerkleRootVO> MerkleRootVO=new ArrayList<>();
            MerkleRootVO = mongoTemplate.find(query, MerkleRootVO.class, collectionName);
            /*if (MerkleRootVO.size() > 0) {
                hashValueList = MerkleRootVO.get(0).getHashValueList();
            } else {
                hashValueList = hashIndexService.getHashValueList(blockHashValue);
                if (hashValueList.size() > 0) {
                    MerkleRootVO merkleRootVO = new MerkleRootVO();
                    merkleRootVO.setHashValueList(hashValueList);
                    merkleRootVO.setRootHashValue(blockHashValue);
                    mongoTemplate.insert(merkleRootVO, "merkleRoot");
                }
            }


            String rootHashValue = MerkleUtil.getMerkleRoot(hashValueList, suite);*/

            List<List<String>> merkleTree=new ArrayList<>();

            if (MerkleRootVO.size() > 0) {
                merkleTree = MerkleRootVO.get(0).getMerkleTree();
            } else {
                hashValueList = hashIndexService.getHashValueList(blockHashValue);
                merkleTree = MerkleUtil.computeMerkleTree(hashValueList, suite);
                if (merkleTree.size() > 0) {
                    MerkleRootVO merkleRootVO = new MerkleRootVO();
                    //merkleRootVO.setHashValueList(hashValueList);
                    merkleRootVO.setMerkleTree(merkleTree);
                    merkleRootVO.setRootHashValue(blockHashValue);
                    mongoTemplate.insert(merkleRootVO, "merkleRoot");
                    /*boolean flag1=mongoTemplate.exists(new Query(),"merkleRoot");
                    if(flag1){
                        mongoTemplate.getCollection("merkleRoot").createIndex(Indexes.ascending("rootHashValue"));
                    }*/
                }
            }
            boolean varifyResult = MerkleUtil.verifyMerkleTree(merkleTree, hashValue, suite);

            if (varifyResult && flag) {
                BlockChainVO blockChainVO=new BlockChainVO();
                blockChainVO.setTxId(hashIndex.getTxId());
                blockChainVO.setHashValue(blockHashValue);
                blockChainVO.setBlockNum(blockNum);
                blockChainVO.setTxTimeStamp(hashIndex.getTxTimeStamp());
                blockChainVO.setUserName(userName);

                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                String txTimeStamp = sdf.format(hashIndex.getTxTimeStamp());

                String successMsg="验证区块链成功,数据位于区块:"+blockNum+",上链时间:"+txTimeStamp+",数据签名人:"+userName;
                return DtoUtil.returnSuccess(successMsg);
            } else {
                return DtoUtil.returnFail("验证区块链失败", "100001");
            }
        }catch (Exception e){
            return DtoUtil.returnFail("验证区块链错误","100003");
        }
    }



    @RequestMapping(value = "/publicVerifyHashValue", method = RequestMethod.POST,
            produces = "application/json")
    @ResponseBody
    public Dto publicVerifyHashValue(@RequestParam(value = "hashValue",required = true) String hashValue) throws Exception {
        try {

            String caType = "msp";
            Long orgIndex = 0L;
            String channelName = "hashchainchannel";
            CryptoPrimitives suite = new CryptoPrimitives();
            suite.init();

            List<HashIndex> hashIndexList = hashIndexService.getHashIndexByHashValue(hashValue);
            HashIndex hashIndex = null;
            if (hashIndexList.size() > 0) {
                hashIndex = hashIndexList.get(0);
            } else {
                return DtoUtil.returnFail("未查询到该交易", "100002");
            }

            String userName=hashIndex.getUserName();

            BlockData blockData = new BlockData();
            Map<String, String> map = blockData.getBlockByNum(hashIndex.getBlockNum(), channelName);
            String blockDataStr = map.get("blockDataStr");
            String blockNum = map.get("blockNum");
            JsonObject blockDataJson = new JsonParser().parse(blockDataStr).getAsJsonObject();
            JsonObject hashItemJson = blockData.getHashItemByTxId(blockDataJson, hashIndex.getTxId());
            String blockHashValue = hashItemJson.get("hashValue").toString();

            blockHashValue = blockHashValue.substring(blockHashValue.indexOf("\"") + 1, blockHashValue.lastIndexOf("\""));
            String blockHashSignature = hashItemJson.get("hashSignature").toString();
            blockHashSignature = blockHashSignature.substring(blockHashSignature.indexOf("\"") + 1, blockHashSignature.lastIndexOf("\""));

            Map<String, Object> pathMap = Util.readUserContext(caType, userName, orgIndex);
            Path caPath = (Path) pathMap.get("caPath");
            Path clientCertPath = (Path) pathMap.get("clientCertPath");
            Path clientKeyPath = (Path) pathMap.get("clientKeyPath");

            CryptoUtil cryptoUtil = new CryptoUtil();

            boolean flag = cryptoUtil.verify(clientCertPath, blockHashSignature, blockHashValue,mongoTemplate);

            String collectionName = "merkleRoot";

            List<String> hashValueList = new ArrayList<>();

            Criteria criteria = Criteria.where("rootHashValue").is(blockHashValue);
            Query query = Query.query(criteria);
            List<MerkleRootVO> MerkleRootVO=new ArrayList<>();
            MerkleRootVO = mongoTemplate.find(query, MerkleRootVO.class, collectionName);
            /*if (MerkleRootVO.size() > 0) {
                hashValueList = MerkleRootVO.get(0).getHashValueList();
            } else {
                hashValueList = hashIndexService.getHashValueList(blockHashValue);
                if (hashValueList.size() > 0) {
                    MerkleRootVO merkleRootVO = new MerkleRootVO();
                    merkleRootVO.setHashValueList(hashValueList);
                    merkleRootVO.setRootHashValue(blockHashValue);
                    mongoTemplate.insert(merkleRootVO, "merkleRoot");
                }
            }


            String rootHashValue = MerkleUtil.getMerkleRoot(hashValueList, suite);*/
            List<List<String>> merkleTree=new ArrayList<>();

            if (MerkleRootVO.size() > 0) {
                merkleTree = MerkleRootVO.get(0).getMerkleTree();
            } else {
                hashValueList = hashIndexService.getHashValueList(blockHashValue);
                merkleTree = MerkleUtil.computeMerkleTree(hashValueList, suite);
                if (merkleTree.size() > 0) {
                    MerkleRootVO merkleRootVO = new MerkleRootVO();
                    //merkleRootVO.setHashValueList(hashValueList);
                    merkleRootVO.setMerkleTree(merkleTree);
                    merkleRootVO.setRootHashValue(blockHashValue);
                    mongoTemplate.insert(merkleRootVO, "merkleRoot");
                    /*boolean flag1=mongoTemplate.exists(new Query(),"merkleRoot");
                    if(flag1){
                        mongoTemplate.getCollection("merkleRoot").createIndex(Indexes.ascending("rootHashValue"));
                    }*/
                }
            }
            boolean varifyResult = MerkleUtil.verifyMerkleTree(merkleTree, hashValue, suite);

            if (varifyResult && flag) {
                BlockChainVO blockChainVO=new BlockChainVO();
                blockChainVO.setTxId(hashIndex.getTxId());
                blockChainVO.setHashValue(blockHashValue);
                blockChainVO.setBlockNum(blockNum);
                blockChainVO.setTxTimeStamp(hashIndex.getTxTimeStamp());
                blockChainVO.setUserName(userName);

                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                String txTimeStamp = sdf.format(hashIndex.getTxTimeStamp());
                String successMsg="验证区块链成功,数据位于区块:"+blockNum+",上链时间:"+txTimeStamp+",数据签名人:"+userName;
                return DtoUtil.returnSuccess(successMsg);
            } else {
                return DtoUtil.returnFail("验证区块链失败", "100001");
            }
        }catch (Exception e){
            e.printStackTrace();
            return DtoUtil.returnFail("验证区块链错误","100003");
        }
    }
}
